#pragma once

#include "menu.hpp"
#include "ui.hpp"


constexpr const char *INTEGER_SIGN_LIST[] = {"+", "-"};   // 整数符号选项
constexpr const char *CHECK_BOX_LIST[] = {"OFF", "ON"};   // 复选框选项
constexpr const char *ERROR_MSG_LIST[] = {"ERR", "VAL"};  // 错误消息占位
constexpr const char *EMPTY_OPTION_LIST[] = {"无值", "NO VALUE"};


constexpr uint16_t constrain_slave_address(uint16_t addr) {
    if (addr < 1) {
        return 1;
    }
    if (addr > 0xf6) {
        return 0xf6;
    }
    return addr;
}


constexpr uint8_t NO_VALUE_ACTION_AUTO_ADDRESS = 0;
constexpr uint8_t NO_VALUE_ACTION_LOAD = 1;

constexpr ConstantStrArray NO_VALUE_ACTION_PROMPT = {"读地址 = ", "读取配置 "};


/**
 * @brief 菜单项编辑界面，也负责发送请求修改从机配置
 *
 */
class EditScreen : public beef::InterfaceBase {
   private:
    // MenuItem &_target_item;  // 目标菜单项

    const char *_title = nullptr;

    SpinBox _boxes[7];  // 用于编辑的SpinBox，最多7个，容纳6 个数字和一个符号
    SpinBoxGroup _box_group;

    MenuItemData _item_data_before_change;
    uint8_t _item_index_before_change;

    uint8_t _ret_code = 0;  // 返回值
    uint8_t _index = 0;     // 当前编辑的SpinBox索引

    bool _lock_edit = false;
    bool _changed = false;
    bool _canceled = false;
    bool _box_changed = false;


    void poll_direction() {
        MenuItem &target_item = menu_items[_index];
        if (button_up.clicked()) {
            if (target_item.is_option() || target_item.is_help()) {
                _box_group.dec();
            }
            else {
                _box_group.inc();
            }

            // reset_button_event();
        }

        if (button_down.clicked()) {
            if (target_item.is_option() || target_item.is_help()) {
                _box_group.inc();
            }
            else {
                _box_group.dec();
            }

            // reset_button_event();
        }

        if (button_left.clicked()) {
            _box_group.focus_left();
        }

        if (button_right.clicked()) {
            _box_group.focus_right();
        }
    }


    virtual void poll() override {
        bool send_buffer = false;

        if (!_lock_edit) {
            // 执行no value 操作时，按钮事件都由主循环处理
            MenuItem &target_item = menu_items[_index];

            poll_direction();

            if (button_ok.clicked()) {
                // 编辑菜单项
                if (target_item.is_option() || target_item.is_check()) {
                    // 选项类型，直接修改索引
                    target_item.index = _box_group.sum();
                }
                else if (target_item.is_integer()) {
                    // 整数类型，重新计算值
                    uint32_t value = _box_group.sum();

                    if (target_item.is_unsigned()) {
                        target_item.data.u = value;
                    }
                    else if (target_item.is_signed()) {
                        target_item.data.s = static_cast<int32_t>(value);
                        if (_boxes[_box_group.count() - 1].current() == 1) {  // 如果符号位是负号
                            target_item.data.s = -target_item.data.s;         // 转换为负数
                        }
                    }
                }

                // 做点输入验证
                // 从机参数的输入验证放在主循环里做，因为要提示输入错误
                switch (_index) {
                    case MENU_INDEX_ADDRESS:
                        // 从机地址的取值范围是1 ~ 246
                        set_menu_slave_address(constrain_slave_address(target_item.data.u));
                        break;

                    case MENU_INDEX_PERIOD:
                        // 连测周期最小200 毫秒，除以100，等于2
                        if (target_item.data.u < 2) {
                            target_item.data.u = 2;
                        }

                    default:
                        break;
                }

                _changed = true;
            }
        }

        if (button_ok.long_pressed()) {
            _canceled = true;
        }

        bool b = _box_group.draw(false);
        if (b) {
            // 开始编辑时，清空错误提示
            _box_changed = true;
            clear_prompt();
        }

        refresh_screen_when(send_buffer, bkr.blink(), b);
    }


    bool auto_address() {
        uint16_t addr = 0;

        // 执行自动取地址
        bool r = read_device_address(this, &addr);

        u8g2.setCursor(74, 35);
        u8g2.setFont(MENU_FONT);
        if (!r) {
            u8g2.print("失败");
        }
        else {
            set_menu_slave_address(addr);
            u8g2.print(addr);
        }
        return r;
    }


    bool load_device_param() {
        uint16_t config_buffer[6];
        bool r = read_config(this, slave_address(), config_buffer);

        u8g2.setCursor(74, 35);
        u8g2.setFont(MENU_FONT);
        if (!r) {
            u8g2.print("失败");
        }
        else {
            u8g2.print("成功");

            // 地址
            // 设备初始值可能是0xffff，超出了spin box 编辑范围，
            // 所以需要把这些初始值调整到可用的范围，又能表示出参数尚未初始化。
            auto addr = config_buffer[0];
            if (addr > 255) {
                addr = 255;
            }
            set_menu_slave_address(addr);

            // 偏置
            auto offset = static_cast<int16_t>(config_buffer[2]);
            if (abs(offset) > 999) {
                offset = 999;
            }
            set_menu_param_offset(offset);

            // 连测间隔
            auto t = config_buffer[3];
            if (t > 999) {
                t = 999;
            }
            set_menu_param_连测间隔(t);

            // 波特率
            auto b = config_buffer[4];
            if (b > 5) {
                b = 2;
            }
            set_menu_param_baud_index(b);

            // 单位
            auto u = config_buffer[5];
            auto uu = unit_number_to_index(u);
            set_menu_param_unit_index(uu);
        }

        return r;
    }


    void call_no_value_action(uint8_t action) {
        bool r = false;
        u8g2.setCursor(20, 35);
        u8g2.setFont(MENU_FONT);
        u8g2.print(NO_VALUE_ACTION_PROMPT[action]);

        _ret_code = NAVIGATION_RETURN_FAIL;

        do {
            clear_box(74, 20, 30, 16);
            bkr.start_blink();
            tick();
            // u8g2.sendBuffer();

            switch (action) {
                case NO_VALUE_ACTION_AUTO_ADDRESS:
                    r = auto_address();
                    break;

                case NO_VALUE_ACTION_LOAD:
                    r = load_device_param();
                    break;
            }

            bkr.stop_blink();
            // tick();
            u8g2.sendBuffer();
            reset_button_event();

            // 如果操作成功，点击ok 退出，否则点击ok 重试
            while (1) {
                tick();

                if (_canceled)
                    return;

                if (r) {
                    if (button_ok.clicked()) {
                        _ret_code = NAVIGATION_RETURN_OK;
                        return;
                    }
                }
                else {
                    if (button_ok.clicked()) {
                        break;
                    }
                }
            }
        } while (1);
    }


    static void put_prompt(const char *p) {
        u8g2.setCursor(21, 50);
        u8g2.setFont(MENU_FONT);
        u8g2.print(p);
    }


    static void clear_prompt() {
        clear_box(21, 37, 80, 14);
    }


    void cancel_edit() {
        MenuItem &target_item = menu_items[_index];

        if (_ret_code == NAVIGATION_RETURN_FAIL) {
            // 操作被取消时，如果发生了更改，但还没有写入从机，就改回去
            target_item.index = _item_index_before_change;
            target_item.data = _item_data_before_change;
        }
    }


    /**
     * @brief 处理菜单编辑操作
     *
     * 【 编辑从机参数时 】：
     *
     * 1. 使用spin box 编辑参数；
     * 2. 点击OK 完成编辑，如果值有误，直接返回编辑；
     * 3. 向从机写入参数；
     * 4. 如果写入失败，再次点击OK 重试，长按OK，取消变更；
     * 5. 按方向键编辑spin box，立即返回第1 步；
     *
     * 在写入参数之前，或写入失败后，长按OK 将取消变更并退出编辑界面。
     * 有可能虽然从机无响应，编辑失败，但变更已经写入从机，软件上无法主动判别这种情况，
     * 每次编辑后都读取并刷新全部参数又有点麻烦，所以只能在菜单界面手动长按OK 强制刷新。
     *
     */
    void process_menu_edit() {
        MenuItem &target_item = menu_items[_index];

        do {
            _ret_code = NAVIGATION_RETURN_FAIL;
            _lock_edit = false;

            // 步骤1，编辑
            while (1) {
                tick();

                if (_canceled) {
                    cancel_edit();
                    return;
                }

                if (_changed) {
                    break;
                }
            }

            _changed = false;
            uint8_t reg_addr = target_item.id;
            uint16_t reg_data = 0;
            bool value_error = false;

            switch (_index) {
                case MENU_INDEX_PARAM_ADDRESS:
                    reg_data = static_cast<uint16_t>(target_item.data.u);
                    if (reg_data != constrain_slave_address(reg_data)) {
                        value_error = true;
                    }
                    break;

                case MENU_INDEX_PARAM_OFFSET:
                    reg_data = static_cast<int16_t>(target_item.data.s);
                    break;

                case MENU_INDEX_PARAM_连测间隔:
                    reg_data = static_cast<uint16_t>(target_item.data.u);
                    break;

                case MENU_INDEX_PARAM_BAUD:
                    reg_data = target_item.index;
                    break;

                case MENU_INDEX_PARAM_UNIT:
                    reg_data = UNIT_NUMBER_LIST[target_item.index];
                    break;

                case MENU_INDEX_BAUD:
                    // 更改波特率后，重新初始化串口
                    init_modbus_client_by_menu();

                default:
                    // 不是从机参数，就直接返回OK
                    _ret_code = NAVIGATION_RETURN_OK;
                    return;
            }

            // 值错误，显示提示，返回编辑
            if (value_error) {
                put_prompt("值错误");
                u8g2.sendBuffer();
                continue;
            }

            // bool rewind = false;
            _box_changed = false;
            _lock_edit = true;

            do {
                // 步骤3，向从机写入参数
                bkr.start_blink();
                tick();
                auto r = write_u16_to(this, slave_address(), reg_addr, reg_data);
                bkr.stop_blink();
                u8g2.sendBuffer();

                // 步骤4，确认、重试、取消
                // 步骤5，或者返回编辑
                // 如果操作成功，点击ok 退出，否则点击ok 重试
                if (r) {
                    switch (_index) {
                        case MENU_INDEX_PARAM_ADDRESS:
                            // 将设置的从机地址同步到系统
                            set_menu_slave_address(reg_data);
                            break;

                        case MENU_INDEX_PARAM_BAUD:
                            // 将设置的从机波特率同步到系统，
                            // 重新初始化串口
                            set_menu_baud_index(reg_data);
                            init_modbus_client_by_menu();
                            break;
                    }

                    _ret_code = NAVIGATION_RETURN_OK;
                    put_prompt("成功");
                    u8g2.sendBuffer();
                }
                else {
                    put_prompt("失败");
                    u8g2.sendBuffer();
                }

                // TODO: 设置波特率

                while (!_box_changed) {
                    tick();
                    poll_direction();

                    if (_canceled) {
                        cancel_edit();
                        return;
                    }

                    if (r) {
                        if (button_ok.clicked()) {
                            return;
                        }
                    }
                    else {
                        if (button_ok.clicked()) {
                            clear_prompt();
                            u8g2.sendBuffer();
                            break;
                        }
                    }
                }
            } while (!_box_changed);
        } while (1);
    }


    virtual int show() override {
        switch (_index) {
            case MENU_INDEX_AUTO_ADDRESS:
                // 进入读从机地址模式
                call_no_value_action(NO_VALUE_ACTION_AUTO_ADDRESS);
                break;

            case MENU_INDEX_LOAD:
                call_no_value_action(NO_VALUE_ACTION_LOAD);
                break;

            default:
                process_menu_edit();
        }

        return _ret_code;
    }


    virtual void init(InterfaceBase *sub) override {
        reset_button_event();
        _ret_code = 0;

        // u8g2.clearBuffer();
        // 编辑界面的显示效果是一个浮在背景界面上的对话框
        bkr.stop_blink();
        draw_right_arrow();
        draw_left_arrow();
        draw_up_arrow();
        draw_down_arrow();

        // 绘制边框
        clear_box(13, 9, 100, 48);
        u8g2.drawBox(14, 15, 98, 39);
        clear_box(15, 16, 96, 37);

        u8g2.setFont(MENU_FONT);
        constexpr uint8_t BOX_X = 21;
        constexpr uint8_t BOX_Y = 32;
        constexpr uint8_t OPTION_W = 80;
        constexpr uint8_t OPTION_SHORT_W = 60;
        constexpr uint8_t OPTION_H = 18;

        if (_index < MENU_ITEM_COUNT) {
            const MenuItem *target_item = &menu_items[_index];

            // 保存未被修改的原始值
            _item_index_before_change = target_item->index;
            _item_data_before_change = target_item->data;

            if (_title == nullptr) {
                _title = target_item->label;
            }

            uint8_t group_right_x = 0;

            // 初始化SpinBox
            if (target_item->is_option() || target_item->is_comment()) {
                // 只需要一个spin box 显示option
                if (target_item->option_count() == 0) {
                    _boxes[0].set_list(EMPTY_OPTION_LIST, 2);
                }
                else {
                    _boxes[0].set_list(target_item->data.list, target_item->option_count(), target_item->option_index());
                }

                _boxes[0].set_placing(BOX_X, BOX_Y, OPTION_W, OPTION_H);  // 设置SpinBox的位置和大小
                _box_group.init_boxes(_boxes, 1);
            }
            else if (target_item->is_check()) {
                // 只需要一个spin box 显示check
                _boxes[0].set_list(CHECK_BOX_LIST, 2, target_item->checked() ? 1 : 0);
                _boxes[0].set_placing(BOX_X, BOX_Y, OPTION_SHORT_W, OPTION_H);  // 设置SpinBox的位置和大小
                _box_group.init_boxes(_boxes, 1);
            }
            else if (target_item->is_integer()) {
                // 数字按照MenuItem 设置的位数显示
                auto l = target_item->number_len();
                if (l > 6) {
                    l = 6;  // 最多6个数字
                }
                else if (l < 1) {
                    l = 1;  // 至少1个数字
                }

                if (target_item->type == menu_type::unsigned_value) {
                    auto v = target_item->data.u;
                    _box_group.init_boxes(_boxes, l);
                    _box_group.set_range(0, 10);  // 设置SpinBox的范围
                    _box_group.split_sum(v, 10);
                }
                else if (target_item->type == menu_type::signed_value) {
                    auto v = target_item->data.s;
                    _box_group.init_boxes(_boxes, l + 1);  // 多一个符号位
                    _box_group.set_range(0, 10);
                    _box_group.split_sum(abs(v), 10);
                    _boxes[l].set_list(INTEGER_SIGN_LIST, 2, v < 0 ? 1 : 0);  // 设置符号位
                    _boxes[l - 1].link(nullptr);                              // 符号位不连接进去
                }
                else if (target_item->type == menu_type::hex_value) {
                    auto v = target_item->data.u;
                    _box_group.init_boxes(_boxes, l);
                    _box_group.set_range(0, 16);  // 十六进制范围是0-15
                    _box_group.split_sum(v, 16);
                    _box_group.set_hex(true);  // 设置SpinBox按16进制显示
                }

                group_right_x = _box_group.set_placing(BOX_X, BOX_Y, 8, 18, 2);
            }
            else if (target_item->is_no_value()) {
                _lock_edit = true;
            }
            else {
                // TODO: 暂时不支持float
                _boxes[0].set_list(ERROR_MSG_LIST, 2, 0);
                _boxes[0].set_placing(BOX_X, BOX_Y, OPTION_SHORT_W, OPTION_H);  // 设置SpinBox的位置和大小
                _box_group.init_boxes(_boxes, 1);
            }

            _box_group.set_focused(0);

            // no value 类型的菜单项作用类似按钮，
            // 代表要立即执行的操作，所以不显示spin box
            if (!_lock_edit) {
                _box_group.draw(true);  // 绘制SpinBoxGroup

                // 绘制要乘的幂
                if (target_item->is_integer()) {
                    auto i = target_item->index;
                    if (i > 0) {
                        // 显示要乘的幂
                        u8g2.setCursor(group_right_x, BOX_Y);
                        u8g2.print('\'');
                        for (; i > 0; --i) {
                            u8g2.print('0');
                        }
                    }
                }
            }
        }
        else {
            _lock_edit = true;
        }

        // 绘制对话框标题
        u8g2.setCursor(18, 18);
        auto w = u8g2.getUTF8Width(_title);
        clear_box(17, 5, w + 2, 15);  // 清除文字背景
        u8g2.print(_title);

        u8g2.sendBuffer();
    }


   public:
    EditScreen(uint8_t index, const char *title) :
        _title(title), _index(index) {}
};